//============================================================================
// Name        : MDeque.h
// Author      : Adam Jozwik
// Version     : 1.0
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#ifndef MDEQUE_H_
#define MDEQUE_H_
#include <iostream>
#include <cstdlib>
#include <sstream>
#include <ctime>
#include <pthread.h>

namespace utils {

class MDequeException: public std::exception {
	const char* errMessage_;
public:
	MDequeException(const char* errMessage) :
			errMessage_(errMessage) {
	}
	const char* what() const throw () {
		return errMessage_;
	}
};

template<typename Item>
class MDeque {

	class Element {
	public:
		Item _item;
		Element * _prev;
		Element * _next;

		Element(Item item, Element * prev, Element * next) {
			_item = item;
			_prev = prev;
			_next = next;
		}
		~Element() {
		}
	};

	int _size;
	Element * _first;
	Element * _last;

	pthread_mutex_t m_mutex;
	pthread_cond_t  m_condv;
public:
	MDeque();
	~MDeque();
	bool isEmpty();
	int size();
	void addFirst(Item item);
	void addLast(Item item);
	void add(Item item);
	Item removeFirst();
	Item removeLast();
	Item remove();
	std::string toString();

	class iterator {
		friend class MDeque;

		Element * ptr;
	public:
		iterator(Element& lnr) :
				ptr(&lnr) {
		}
		iterator(Element* p = nullptr) :
				ptr(p) {
		}
		iterator& operator++() {
			ptr = ptr->_next;
			return *this;
		} //pre
		iterator operator++(int a) {
			iterator retval = *this;
			++*this;
			return retval;
		} //post
		iterator& operator--() {
			ptr = ptr->_prev;
			return *this;
		} //pre
		iterator operator--(int a) {
			iterator retval = *this;
			--*this;
			return retval;
		} //post
		Item& operator*() const {
			return ptr->_item;
		}
		bool operator==(const iterator& rhs) const {
			return ptr == rhs.ptr;
		}
		bool operator!=(const iterator& rhs) const {
			return ptr != rhs.ptr;
		}
	};
	iterator begin() const {
		return iterator(_first);
	}
	iterator end() const {
		return iterator(_last == nullptr ? nullptr : _last->_next);
	}
};

template<class Item> MDeque<Item>::MDeque() : _size(0), _first(nullptr), _last(nullptr) {
	std::cout << "MDeque<Item>\n";
	pthread_mutex_init(&m_mutex, NULL);
	pthread_cond_init(&m_condv, NULL);
}

template<class Item> MDeque<Item>::~MDeque() {
	if (_size > 0)
		std::cout << "delete: ";
	while (_size > 0) {
		Item i = _first->_item;
		if (_size > 1) {
			_first = _first->_next;
			delete _first->_prev;
			_first->_prev = 0;
		} else {
			delete _first;
			_first = 0;
			_last = 0;
		}
		_size--;
		std::cout << "--" << i << " ";
	}
	pthread_mutex_destroy(&m_mutex);
	pthread_cond_destroy(&m_condv);
}
template<class Item> bool MDeque<Item>::isEmpty() {
	pthread_mutex_lock(&m_mutex);
	bool isEmpty = _size == 0 ? true : false;
	pthread_mutex_unlock(&m_mutex);
	return isEmpty;
}
template<class Item> int MDeque<Item>::size() {
	pthread_mutex_lock(&m_mutex);
	int size = _size;
	pthread_mutex_unlock(&m_mutex);
	return size;
}

template<class Item> void MDeque<Item>::addFirst(Item item) {
	//if(&item == nullptr) throw MDequeException("NullPointerException");
	pthread_mutex_lock(&m_mutex);
	if (_size < 1) {
		_first = new Element(item, nullptr, nullptr);
		_last = _first;
		_size = 1;
	} else {
		_first = new Element(item, nullptr, _first);
		_first->_next->_prev = _first;
		_size++;
	}
	//std::cout << "++" << item << " ";
	pthread_cond_signal(&m_condv);
	pthread_mutex_unlock(&m_mutex);
}

template<class Item> void MDeque<Item>::add(Item item) {
	return addFirst(item);
}

template<class Item> void MDeque<Item>::addLast(Item item) {
	//if(&item == nullptr)  throw MDequeException("NullPointerException");
	pthread_mutex_lock(&m_mutex);
	if (_size < 1) {
		_last = new Element(item, nullptr, nullptr);
		_first = _last;
		_size = 1;
	} else {
		_last = new Element(item, _last, nullptr);
		_last->_prev->_next = _last;
		_size++;
	}
	//std::cout << item << "++ ";
	pthread_cond_signal(&m_condv);
	pthread_mutex_unlock(&m_mutex);
}

template<class Item> Item MDeque<Item>::removeFirst() {
	pthread_mutex_lock(&m_mutex);
//	if(isEmpty()) {
//		throw MDequeException("NoSuchElementException");
//	}
	while (_size == 0) {
		pthread_cond_wait(&m_condv, &m_mutex);
	}
	Item i = _first->_item;
	if (_size > 1) {
		_first = _first->_next;
		delete _first->_prev;
		_first->_prev = 0;
	} else {
		delete _first;
		_first = 0;
		_last = 0;
	}
	_size--;
	//std::cout << "--" << i << " ";
	pthread_mutex_unlock(&m_mutex);
	return i;
}

template<class Item> Item MDeque<Item>::removeLast() {
	pthread_mutex_lock(&m_mutex);
//	if(isEmpty()) {
//		throw MDequeException("NoSuchElementException");
//	}
	while (_size == 0) {
		pthread_cond_wait(&m_condv, &m_mutex);
	}

	Item i = _last->_item;
	if (_size > 1) {
		_last = _last->_prev;
		delete _last->_next;
		_last->_next = 0;
	} else {
		delete _last;
		_first = 0;
		_last = 0;
	}
	_size--;
	//std::cout << i << "-- ";
	pthread_mutex_unlock(&m_mutex);
	return i;
}

template<class Item> Item MDeque<Item>::remove() {
	return removeLast();
}

template<class Item> std::string MDeque<Item>::toString() {
	pthread_mutex_lock(&m_mutex);
	std::stringstream ss;
	ss << "[ ";
	for (MDeque<Item>::iterator it = this->begin(); it != this->end(); ++it) {
		ss << *it << " ";
	}
	ss << "]";
	pthread_mutex_unlock(&m_mutex);
	return ss.str();
}
int MDeque_main() {

	try {
		MDeque<int> * d = new MDeque<int>();

		std::cout << "new size: " << d->size() << " is empty: "
				<< (d->isEmpty() == 1 ? 'Y' : 'N') << " " << d->toString()
				<< std::endl;
		d->addFirst(1);
		std::cout << "size: " << d->size() << " is empty: "
				<< (d->isEmpty() == 1 ? 'Y' : 'N') << " " << d->toString()
				<< std::endl;
		d->addFirst(2);
		std::cout << "size: " << d->size() << " is empty: "
				<< (d->isEmpty() == 1 ? 'Y' : 'N') << " " << d->toString()
				<< std::endl;
		d->addFirst(3);
		std::cout << "size: " << d->size() << " is empty: "
				<< (d->isEmpty() == 1 ? 'Y' : 'N') << " " << d->toString()
				<< std::endl;
		d->addFirst(4);
		std::cout << "size: " << d->size() << " is empty: "
				<< (d->isEmpty() == 1 ? 'Y' : 'N') << " " << d->toString()
				<< std::endl;
		d->addFirst(5);
		std::cout << "size: " << d->size() << " is empty: "
				<< (d->isEmpty() == 1 ? 'Y' : 'N') << " " << d->toString()
				<< std::endl;
//		d->removeFirst();
//		cout << "size: " << d->size() << " is empty: " << (d->isEmpty()==1?'Y':'N') << " " << d->toString() << endl;
		d->addLast(1);
		std::cout << "size: " << d->size() << " is empty: "
				<< (d->isEmpty() == 1 ? 'Y' : 'N') << " " << d->toString()
				<< std::endl;
//		d->removeFirst();
//		cout << "size: " << d->size() << " is empty: " << (d->isEmpty()==1?'Y':'N') << " " << d->toString() << endl;
//		d->removeLast();
//		cout << "size: " << d->size() << " is empty: " << (d->isEmpty()==1?'Y':'N') << " " << d->toString() << endl;
//		d->removeLast();
//		cout << "size: " << d->size() << " is empty: " << (d->isEmpty()==1?'Y':'N') << " " << d->toString() << endl;
		d->addLast(10);
		std::cout << "size: " << d->size() << " is empty: "
				<< (d->isEmpty() == 1 ? 'Y' : 'N') << " " << d->toString()
				<< std::endl;
//		d->removeLast();
//		cout << "size: " << d->size() << " is empty: " << (d->isEmpty()==1?'Y':'N') << " " << d->toString() << endl;
//		d->removeLast();
//		cout << "size: " << d->size() << " is empty: " << (d->isEmpty()==1?'Y':'N') << " " << d->toString() << endl;
		delete d;

	} catch (const std::exception& e) {
		std::cout << "Exception is: " << e.what() << std::endl;
	}

//	clock_t begin = clock();
//	clock_t end = clock();
//	double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;

	return 0;
}

} /* namespace utils */

#endif /* MDEQUE_H_ */
